{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 面向对象"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'\\n\\nempCount 变量是一个类变量，它的值将在这个类的所有实例之间共享。你可以在内部类或外部类使用 Employee.empCount 访问。\\n\\n第一种方法__init__()方法是一种特殊的方法，被称为类的构造函数或初始化方法，当创建了这个类的实例时就会调用该方法\\n\\nself 代表类的实例，self 在定义类的方法时是必须有的，虽然在调用时不必传入相应的参数。\\n\\n\\n'"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class Employee:\n",
    "    '所有员工的基类'\n",
    "    empCount = 0\n",
    "    def _init_(self,name,salary):\n",
    "        self.name = name\n",
    "        self.salary = salary\n",
    "        Employee.empCount += 1\n",
    "def displayCount(self):\n",
    "    print (\"Total Employee %d\" % Employee.empCount)\n",
    "def displayEmployee(self):\n",
    "    print (\"Name : \", self.name,  \", Salary: \", self.salary )\n",
    "    \n",
    "'''\n",
    "\n",
    "empCount 变量是一个类变量，它的值将在这个类的所有实例之间共享。你可以在内部类或外部类使用 Employee.empCount 访问。\n",
    "\n",
    "第一种方法__init__()方法是一种特殊的方法，被称为类的构造函数或初始化方法，当创建了这个类的实例时就会调用该方法\n",
    "\n",
    "self 代表类的实例，self 在定义类的方法时是必须有的，虽然在调用时不必传入相应的参数。\n",
    "\n",
    "\n",
    "'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<__main__.Test object at 0x000001359B64D128>\n",
      "<class '__main__.Test'>\n"
     ]
    }
   ],
   "source": [
    "'''\n",
    "\n",
    "self代表类的实例，而非类\n",
    "类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。\n",
    "\n",
    "'''\n",
    "\n",
    "\n",
    "class Test:\n",
    "    def prt(self):\n",
    "        print(self)\n",
    "        print(self.__class__)\n",
    "\n",
    "t = Test()\n",
    "t.prt()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<__main__.Test object at 0x000001359B6DE668>\n",
      "<class '__main__.Test'>\n"
     ]
    }
   ],
   "source": [
    "'''\n",
    "\n",
    "从执行结果可以很明显的看出，self 代表的是类的实例，代表当前对象的地址，而 self.__class__ 则指向类。\n",
    "\n",
    "self 不是 python 关键字，我们把他换成 runoob 也是可以正常执行的:\n",
    "\n",
    "'''\n",
    "\n",
    "\n",
    "class Test:\n",
    "    def prt(runoob):\n",
    "        print(runoob)\n",
    "        print(runoob.__class__)\n",
    "        \n",
    "t = Test()\n",
    "t.prt()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 创建实例对象"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "'''\n",
    "\n",
    "实例化类其他编程语言中一般用关键字 new，但是在 Python 中并没有这个关键字，类的实例化类似函数调用方式。\n",
    "\n",
    "以下使用类的名称 Employee 来实例化，并通过 __init__ 方法接收参数。\n",
    "\n",
    "'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Name :  Zara , Salary:  4000\n",
      "Name :  Manni , Salary:  6000\n",
      "Total Employee 2\n"
     ]
    }
   ],
   "source": [
    "class Employee:\n",
    "    '所有员工的基类'\n",
    "    empCount = 0\n",
    "    def __init__(self,name,salary):\n",
    "        self.name = name\n",
    "        self.salary = salary\n",
    "        Employee.empCount += 1\n",
    "    def displayCount(self):\n",
    "        print (\"Total Employee %d\" % Employee.empCount)\n",
    "    def displayEmployee(self):\n",
    "        print (\"Name : \", self.name,  \", Salary: \", self.salary )\n",
    "    \n",
    "# \"创建 Employee 类的第一个对象\"\n",
    "emp1 = Employee(\"Zara\", 4000)\n",
    "# \"创建 Employee 类的第二个对象\"\n",
    "emp2 = Employee(\"Manni\", 6000)\n",
    "\n",
    "\n",
    "'''\n",
    "\n",
    "访问属性\n",
    "您可以使用点号 . 来访问对象的属性。使用如下类的名称访问类变量:\n",
    "\n",
    "'''\n",
    "emp1.displayEmployee()\n",
    "emp2.displayEmployee()\n",
    "print( \"Total Employee %d\" % Employee.empCount)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "#  添加，删除，修改类的属性\n",
    "\n",
    "emp1.age = 7  # 添加一个 'age' 属性\n",
    "emp1.age = 8  # 修改 'age' 属性\n",
    "del emp1.age  # 删除 'age' 属性\n",
    "\n",
    "'''\n",
    "\n",
    "可以使用以下函数的方式来访问属性：\n",
    "\n",
    "getattr(obj, name[, default]) : 访问对象的属性。\n",
    "hasattr(obj,name) : 检查是否存在一个属性。\n",
    "setattr(obj,name,value) : 设置一个属性。如果属性不存在，会创建一个新属性。\n",
    "delattr(obj, name) : 删除属性。\n",
    "\n",
    "\n",
    "'''\n",
    "\n",
    "hasattr(emp1, 'age')    # 如果存在 'age' 属性返回 True。\n",
    "# getattr(emp1, 'age')    # 报错 ： 'Employee' object has no attribute 'age'\n",
    "\n",
    "setattr(emp1, 'age', 8) # 添加属性 'age' 值为 8\n",
    "delattr(emp1, 'age')    # 删除属性 'age'\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Python内置类属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "__dict__  类的属性 :  {'__module__': '__main__', '__doc__': '所有员工的基类', 'empCount': 0, '__init__': <function Employee.__init__ at 0x000002057C2146A8>, 'displayCount': <function Employee.displayCount at 0x000002057C214598>, 'displayEmployee': <function Employee.displayEmployee at 0x000002057C214510>, '__dict__': <attribute '__dict__' of 'Employee' objects>, '__weakref__': <attribute '__weakref__' of 'Employee' objects>}\n",
      "__doc__ 类的文档字符串:  所有员工的基类\n",
      "__name__ 类名 ：  Employee\n",
      "__module__  类定义所在的模块 ：  __main__\n",
      "__bases__  类的所有父类构成元素 ：  (<class 'object'>,)\n"
     ]
    }
   ],
   "source": [
    "'''\n",
    "\n",
    "__dict__ : 类的属性（包含一个字典，由类的数据属性组成）\n",
    "__doc__ :类的文档字符串\n",
    "__name__: 类名\n",
    "__module__: 类定义所在的模块（类的全名是'__main__.className'，如果类位于一个导入模块mymod中，那么className.__module__ 等于 mymod）\n",
    "__bases__ : 类的所有父类构成元素（包含了一个由所有父类组成的元组）\n",
    "\n",
    "\n",
    "'''\n",
    "class Employee:\n",
    "    '所有员工的基类'\n",
    "    empCount = 0\n",
    "    def __init__(self,name,salary):\n",
    "        self.name = name\n",
    "        self.salary = salary\n",
    "        Employee.empCount += 1\n",
    "    def displayCount(self):\n",
    "        print (\"Total Employee %d\" % Employee.empCount)\n",
    "    def displayEmployee(self):\n",
    "        print (\"Name : \", self.name,  \", Salary: \", self.salary )\n",
    "    \n",
    "print('__dict__  类的属性 : ',Employee.__dict__)\n",
    "print('__doc__ 类的文档字符串: ',Employee.__doc__)\n",
    "print('__name__ 类名 ： ',Employee.__name__)\n",
    "print('__module__  类定义所在的模块 ： ',Employee.__module__)\n",
    "print('__bases__  类的所有父类构成元素 ： ',Employee.__bases__)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### python对象销毁(垃圾回收)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "id1 =  1329760484600\n",
      "id2 =  1329760484600\n",
      "id3 =  1329760484600\n",
      "Point 已经销毁\n"
     ]
    }
   ],
   "source": [
    "'''\n",
    "\n",
    "垃圾回收机制不仅针对引用计数为0的对象，同样也可以处理循环引用的情况。循环引用指的是，两个对象相互引用，但是没有其他变量引用他们。\n",
    "这种情况下，仅使用引用计数是不够的。Python 的垃圾收集器实际上是一个引用计数器和一个循环垃圾收集器。作为引用计数的补充， 垃圾收集器也会留心被分配的总量很大（及未通过引用计数销毁的那些）的对象。\n",
    "在这种情况下， 解释器会暂停下来， 试图清理所有未引用的循环。\n",
    "\n",
    "'''\n",
    "\n",
    "\n",
    "class Point:\n",
    "    def __init__(self,x = 0, y = 0):\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "    \n",
    "    def __del__(self):\n",
    "        class_name = self.__class__.__name__\n",
    "        print(class_name,'已经销毁')\n",
    "        \n",
    "        \n",
    "        \n",
    "p1 = Point()\n",
    "p2 = p1\n",
    "p3 = p1\n",
    "\n",
    "print('id1 = ',id(p1))\n",
    "print('id2 = ',id(p2))\n",
    "print('id3 = ',id(p3))\n",
    "\n",
    "del p1\n",
    "del p2\n",
    "del p3\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 类的继承"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "调用子类构造函数\n",
      "我是子类的方法\n",
      "调用父类方法\n",
      "父类属性 ：  200\n"
     ]
    }
   ],
   "source": [
    "'''\n",
    "\n",
    "\n",
    "面向对象的编程带来的主要好处之一是代码的重用，实现这种重用的方法之一是通过继承机制。\n",
    "\n",
    "通过继承创建的新类称为子类或派生类，被继承的类称为基类、父类或超类。\n",
    "\n",
    "继承语法\n",
    "\n",
    "class 派生类名(基类名)\n",
    "\n",
    "在python中继承中的一些特点：\n",
    "\n",
    "1、如果在子类中需要父类的构造方法就需要显示的调用父类的构造方法，或者不重写父类的构造方法。详细说明可查看：python 子类继承父类构造函数说明。\n",
    "2、在调用基类的方法时，需要加上基类的类名前缀，且需要带上 self 参数变量。区别在于类中调用普通函数时并不需要带上 self 参数\n",
    "3、Python 总是首先查找对应类型的方法，如果它不能在派生类中找到对应的方法，它才开始到基类中逐个查找。（先在本类中查找调用的方法，找不到才去基类中找）。\n",
    "如果在继承元组中列了一个以上的类，那么它就被称作\"多重继承\" 。\n",
    "\n",
    "语法：\n",
    "\n",
    "派生类的声明，与他们的父类类似，继承的基类列表跟在类名之后，如下所示：\n",
    "\n",
    "class SubClassName (ParentClass1[, ParentClass2, ...]):\n",
    "\n",
    "\n",
    "'''\n",
    "\n",
    "\n",
    "class Parent:                      #  定义父类\n",
    "    parentAttr = 100\n",
    "    def __init__(self):\n",
    "        print('调用父类构造函数')\n",
    "    \n",
    "    def parentMethod(self):\n",
    "        print('调用父类方法')\n",
    "        \n",
    "    def setAttr(self,attr):\n",
    "        Parent.parentAttr = attr\n",
    "        \n",
    "    def getAttr(self):\n",
    "        print('父类属性 ： ', Parent.parentAttr)\n",
    "        \n",
    "class Child(Parent):            # 定义子类\n",
    "    def __init__(self):\n",
    "        print('调用子类构造函数')\n",
    "        \n",
    "    def childMethod(self):\n",
    "        print('我是子类的方法')\n",
    "        \n",
    "    \n",
    "c = Child()          # 实例化子类\n",
    "c.childMethod()      # 调用子类的方法\n",
    "c.parentMethod()     # 调用父类方法\n",
    "c.setAttr(200)       # 再次调用父类的方法 - 设置属性值\n",
    "c.getAttr()          # 再次调用父类的方法 - 获取属性值\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A类\n",
      "B类\n",
      "C 类\n",
      "C类的方法\n",
      "result =  True\n",
      "result2 =  False\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'\\n\\n\\n可以使用issubclass()或者isinstance()方法来检测。\\n\\nissubclass() - 布尔函数判断一个类是另一个类的子类或者子孙类，语法：issubclass(sub,sup)\\nisinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。\\n\\n'"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#  可以继承多个类\n",
    "\n",
    "class A:        # 定义类 A\n",
    "    def __init__(self):\n",
    "        print('A类')\n",
    "\n",
    "\n",
    "class B:         # 定义类 B\n",
    "    def __init__(self):\n",
    "        print('B类')\n",
    "\n",
    "\n",
    "class C(A,B):\n",
    "    def __init__(self):\n",
    "        print('C 类')\n",
    "        \n",
    "    def cMethod(self):\n",
    "        print('C类的方法')\n",
    "        \n",
    "a = A()\n",
    "b = B()\n",
    "c = C()\n",
    "c.cMethod()\n",
    "\n",
    "result = isinstance(c,A)\n",
    "result2 = issubclass(B,A)\n",
    "print('result = ',result)\n",
    "print('result2 = ',result2)\n",
    "    \n",
    "'''\n",
    "\n",
    "\n",
    "可以使用issubclass()或者isinstance()方法来检测。\n",
    "\n",
    "isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。\n",
    "\n",
    "issubclass() - 布尔函数判断一个类是另一个类的子类或者子孙类，语法：issubclass(sub,sup)\n",
    "\n",
    "'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "调用子类方法\n"
     ]
    }
   ],
   "source": [
    "### 方法重写\n",
    "class Parent:        # 定义父类\n",
    "    def myMethod(self):\n",
    "        print('调用父类方法')\n",
    "class Child(Parent): # 定义子类\n",
    "    def myMethod(self):\n",
    "        print('调用子类方法')\n",
    " \n",
    "c = Child()          # 子类实例\n",
    "c.myMethod()         # 子类调用重写方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Vector (7, 8)\n"
     ]
    }
   ],
   "source": [
    "#  运算符重载\n",
    "\n",
    "class Vector:\n",
    "    def __init__(self, a, b):\n",
    "        self.a = a\n",
    "        self.b = b\n",
    " \n",
    "    def __str__(self):\n",
    "        return 'Vector (%d, %d)' % (self.a, self.b)\n",
    "   \n",
    "    def __add__(self,other):\n",
    "        return Vector(self.a + other.a, self.b + other.b)\n",
    " \n",
    "v1 = Vector(2,10)\n",
    "v2 = Vector(5,-2)\n",
    "print(v1 + v2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n",
      "2\n"
     ]
    },
    {
     "ename": "AttributeError",
     "evalue": "'JustCounter' object has no attribute '__secretCount'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-69-8c2d8cface1e>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m     30\u001b[0m \u001b[0mcounter\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcount\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     31\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mcounter\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpublicCount\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 32\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mcounter\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__secretCount\u001b[0m\u001b[1;33m)\u001b[0m  \u001b[1;31m# 报错，实例不能访问私有变量\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m: 'JustCounter' object has no attribute '__secretCount'"
     ]
    }
   ],
   "source": [
    "# 类属性与方法\n",
    "\n",
    "'''\n",
    "\n",
    "\n",
    "类的私有属性\n",
    "__private_attrs：两个下划线开头，声明该属性为私有，不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs。\n",
    "\n",
    "类的方法\n",
    "在类的内部，使用 def 关键字可以为类定义一个方法，与一般函数定义不同，类方法必须包含参数 self,且为第一个参数\n",
    "\n",
    "类的私有方法\n",
    "__private_method：两个下划线开头，声明该方法为私有方法，不能在类的外部调用。在类的内部调用 self.__private_methods\n",
    "\n",
    "\n",
    "'''\n",
    "\n",
    "\n",
    "class JustCounter:\n",
    "    __secretCount = 0  # 私有变量\n",
    "    publicCount = 0    # 公开变量\n",
    " \n",
    "    def count(self):\n",
    "        self.__secretCount += 1\n",
    "        self.publicCount += 1\n",
    "        print(self.__secretCount)\n",
    " \n",
    "counter = JustCounter()\n",
    "counter.count()\n",
    "counter.count()\n",
    "print(counter.publicCount)\n",
    "print(counter.__secretCount)  # 报错，实例不能访问私有变量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "www.runoob.com\n"
     ]
    }
   ],
   "source": [
    "# Python不允许实例化的类访问私有数据，但你可以使用 object._className__attrName（ 对象名._类名__私有属性名 ）访问属性\n",
    "\n",
    "class Runoob:\n",
    "    __site = \"www.runoob.com\"\n",
    "\n",
    "runoob = Runoob()\n",
    "print(runoob._Runoob__site)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "'''\n",
    "单下划线、双下划线、头尾双下划线说明：\n",
    "\n",
    "__foo__: 定义的是特殊方法，一般是系统定义名字 ，类似 __init__() 之类的。\n",
    "\n",
    "_foo: 以单下划线开头的表示的是 protected 类型的变量，即保护类型只能允许其本身与子类进行访问，不能用于 from module import *\n",
    "\n",
    "__foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。\n",
    "\n",
    "\n",
    "\n",
    "'''"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
